import { defaultTagRE, ELEMENT_TYPE, parseHTML } from "./parse";

// 生成props
function genProps(attrs) {
  let str = '';
  for (let i = 0; i < attrs.length; i++) {
    let attr = attrs[i];
    // 特殊处理style,变成对象形式
    if (attr.name === 'style') {
      const obj = {};
      attr.value.split(';').forEach(item => {
        const [key, value] = item.split(':');
        obj[key] = value;
      });
      attr.value = obj;
    }
    // 拼接属性 key:value
    str += `${attr.name}:${JSON.stringify(attr.value)},`;
  }
  return `{${str.slice(0, -1)}}`;
}

// 将node生成render代码
function gen(node) {
  if (node.type === ELEMENT_TYPE) {
    // 如果是元素,就接着生成
    return codegen(node);
  } else {
    // 文本
    let text = node.text;
    if (!defaultTagRE.test(text)) {
      // 如果没有{{}}就直接生成文本节点
      return `_v(${JSON.stringify(text)})`
    } else {
      // 否则就用_s处理{{}}内容,_v(_s(name)+'hello'+_s(age))
      const tokens = [];
      let match;
      // 重置为0,不然因为正则里有g匹配有问题
      defaultTagRE.lastIndex = 0;
      // 记录每次正则的结束位置
      let lastIndex = 0;
      while (match = defaultTagRE.exec(text)) {
        // 匹配位置的索引
        const index = match.index;
        if (index > lastIndex) {
          // 如果符合说明这段内容是普通文本,直接存起来
          tokens.push(JSON.stringify(text.slice(lastIndex, index)));
        }
        // 把正则匹配到的模板扔进token里 {{ name }}
        tokens.push(`_s(${match[1].trim()})`);
        // 更新下标位置
        lastIndex = index + match[0].length;
      }
      if (lastIndex < text.length) {
        // 把剩下的扔token里
        tokens.push(JSON.stringify(text.slice(lastIndex)));
      }
      return `_v(${tokens.join('+')})`;
    }
  }
}
// 生成孩子方法
function genChildren(children) {
  return children.map(child => gen(child)).join(',');
}

// 生成标签类型节点,_c('标签名','{属性}/null','children1(可选)','children2(可选)')
function codegen(ast) {
  let children = genChildren(ast.children);
  let code = `_c("${ast.tag}",${ast.attrs.length > 0 ?
    genProps(ast.attrs) : null
    }${ast.children.length ? `,${children}` : ''})`;
  return code;
}

export function compileToFunction(template) {
  // 将template转换成ast语法树
  const ast = parseHTML(template);
  // 生成代码字符串
  let code = codegen(ast);
  // 生成render,模板引擎原理就是 with + new Function
  code = `with(this){return ${code}}`;
  const render = new Function(code);
  return render;
}